/*
 * Copyright (C),2015,北京新诺创科软件技术有限公司
 * author zhangmengliang
 */
package com.xnck.mfpms.service;

import com.xiaoleilu.hutool.DateUtil;
import com.xiaoleilu.hutool.StrUtil;
import com.xnck.mfpms.dao.*;
import com.xnck.mfpms.entity.*;
import com.xnck.mfpms.exception.ValidateException;
import com.xnck.mfpms.util.PagerUtil;
import com.xnck.mfpms.util.ValidatorUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.sql.Criteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class ActionService {

    @Autowired
    private ActionDao actionDao;

    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private ResourceDao resDao;

    @Autowired
    private RoleActionDao roleActionDao;

    @Autowired
    private UserActionDao userActionDao;

    @Autowired
    private ActionResDao actionResDao;

    @Autowired
    private ActionChildDao actionChildDao;

    public ActionInfo get(String actionId){
        return actionDao.get(actionId);
    }

    public int getCount(String name, String parentId){
        Criteria cri = Cnd.cri();
        this.getSearchCnd(cri, name, parentId);
        return actionDao.searchCount(cri);
    }

    public List<ActionInfo> gets(String name, String parentId, String orderName, String orderType,
                                 int pageSize, int beginIndex){
        Criteria cri = Cnd.cri();
        this.getSearchCnd(cri, name, parentId);
        PagerUtil.getSearchOrder(cri, ActionInfo.FIELD_PARENTID, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return actionDao.searchByPage(cri, currentPage, pageSize);
    }

    public List<Map<String, Object>> getNodesByPId(String parentId){
        List<ActionInfo> actions = actionDao.search(Cnd.where(ActionInfo.FIELD_PARENTID, "=", parentId));
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
        for (ActionInfo action : actions) {
            Map<String, Object> child = new HashMap<String, Object>();
            child.put("id", action.getId());
            if (action.getChildcount() > 0) {
                child.put("isParent", true);
            }
            else {
                child.put("isParent", false);
            }
            child.put("name", action.getDisplayname());
            result.add(child);
        }
        return result;
    }

    public List<Map<String, Object>> getAllNodes() throws Exception {
        List<ActionInfo> actions = actionDao.search(Cnd.orderBy().asc(ActionInfo.FIELD_DISPLAYNAME));
        List<Map<String, Object>> nodes = new ArrayList<Map<String, Object>>();
        for (ActionInfo action: actions) {
            Map<String, Object> node = new HashMap<String, Object>();
            node.put("id", action.getId());
            node.put("name", action.getDisplayname());
            node.put("pId", action.getParentid());
            nodes.add(node);
        }
        return nodes;
    }

    @Transactional(rollbackFor = Exception.class)
    public void create(String creatorId, String displayName, String parentId, String remark, boolean enable) throws Exception {
        this.checkActionInsertInput(creatorId, displayName, parentId, remark);
        String actionId = UUID.randomUUID().toString();
        Date addTime = DateUtil.date();
        ActionInfo action = actionDao.insert(actionId, displayName, parentId, remark, enable, addTime, creatorId);
        //写入父级追踪信息
        ActionInfo parentAction = actionDao.get(parentId);
        if (null != parentAction){
            actionDao.findLink(parentAction, ActionInfo.FIELD_ALL_PARENTS);
            action.setAllparents(parentAction.getAllparents());
            action.getAllparents().add(parentAction);
            actionDao.insertRelation(action, ActionInfo.FIELD_ALL_PARENTS);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void update(String id, String displayName, String parentId, String remark, boolean enable) throws Exception {
        this.checkActionUpdateInput(displayName, parentId, remark);
        ActionInfo action = actionDao.get(id);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        actionDao.findLink(action, ActionInfo.FIELD_PARENT);
        if (null != action.getParent() && !action.getParent().isEnable()){
            throw new ValidateException("父权限处于禁用状态");
        }
        //清除父级追踪信息
        actionDao.clearLinks(action, ActionInfo.FIELD_ALL_PARENTS);
        //写入父级追踪信息
        ActionInfo parentAction = actionDao.get(parentId);
        if (null != parentAction){
            actionDao.findLink(parentAction, ActionInfo.FIELD_ALL_PARENTS);
            action.setAllparents(parentAction.getAllparents());
            action.getAllparents().add(parentAction);
            actionDao.insertRelation(action, ActionInfo.FIELD_ALL_PARENTS);
        }
        //禁用所有子权限
        if (!enable){
            actionDao.findLink(action, ActionInfo.FIELD_ALL_CHILDS);
            for (ActionInfo child: action.getAllchilds()) {
                child.setEnable(enable);
                actionDao.update(child);
            }
        }
        //更新权限
        action.setParentid(parentId);
        action.setRemark(remark);
        action.setDisplayname(displayName);
        action.setEnable(enable);
        actionDao.update(action);
    }

    /**
     * 删除权限
     * @param actionIds
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public void deletes(String actionIds) throws Exception {
        int childCount = actionChildDao.searchCount(Cnd.where(ActionChild.FIELD_PARENTID, "in", actionIds.split(",")));
        if (childCount > 0){
            throw new ValidateException("包含子级权限，不可删除");
        }
        actionDao.clear(Cnd.where(ActionInfo.FIELD_ID, "in", actionIds.split(",")));
        actionChildDao.clear(Cnd.where(ActionChild.FIELD_CHILDID, "in", actionIds.split(",")));
        userActionDao.clear(Cnd.where(UserAction.FIELD_ACTIONID, "in", actionIds.split(",")));
        roleActionDao.clear(Cnd.where(RoleAction.FIELD_ACTIONID, "in", actionIds.split(",")));
        actionResDao.clear(Cnd.where(ActionResource.FIELD_ACTIONID, "in", actionIds.split(",")));
    }

    /**
     * 已关联角色数量
     * @param actionId
     * @param roleName
     * @return
     */
    public int getJoinedRolesCount(String actionId, String roleName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(roleName)) {
            cri.where().andLike(RoleInfo.FIELD_DISPLAYNAME, roleName);
        }
        return roleDao.getCountJoinAction(actionId, cri);
    }

    /**
     * 未关联角色数量
     * @param actionId
     * @param roleName
     * @return
     */
    public int getNoJoinRolesCount(String actionId, String roleName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(roleName)) {
            cri.where().andLike(RoleInfo.FIELD_DISPLAYNAME, roleName);
        }
        return roleDao.getCountNotJoinAction(actionId, cri);
    }

    /**
     * 已关联角色
     * @return
     * @throws Exception
     */
    public List<RoleInfo> getJoinedRoles(String actionId, String roleName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) {
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(roleName)) {
            cri.where().andLike(RoleInfo.FIELD_DISPLAYNAME, roleName);
        }
        PagerUtil.getSearchOrder(cri, RoleInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return roleDao.getJoinAction(actionId, cri, currentPage, pageSize);
    }

    /**
     * 未已关联角色
     * @return
     * @throws Exception
     */
    public List<RoleInfo> getNoJoinRoles(String actionId, String roleName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(roleName)) {
            cri.where().andLike(RoleInfo.FIELD_DISPLAYNAME, roleName);
        }
        PagerUtil.getSearchOrder(cri, RoleInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return roleDao.getNotJoinAction(actionId, cri, currentPage, pageSize);
    }

    /**
     * 关联权限和角色
     * @param actionId 被关联权限
     * @param roleIdStr 待关联角色列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinRole(String actionId, String roleIdStr) throws Exception {
        if (StrUtil.isBlank(roleIdStr)){
            throw new ValidateException("角色不存在");
        }
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        String[] roleIds = roleIdStr.split(",");
        actionDao.findLink(action, ActionInfo.FIELD_ROLES);
        List<String> roleIdList = this.getRoleIdNotJoin(roleIds, action.getRoles());
        if (roleIdList.size() > 0){
            List<RoleInfo> roles = roleDao.search(Cnd.where(RoleInfo.FIELD_ID, "in", roleIdList.toArray(new String[roleIdList.size()])));
            if (roles.size() > 0){
                action.setRoles(roles);
                actionDao.insertRelation(action, ActionInfo.FIELD_ROLES);
            }
        }
    }

    /**
     * 取消关联权限和角色
     * @param actionId 被取消关联的权限
     * @param roleIdStr 被关联角色列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelJoinRole(String actionId, String roleIdStr) throws Exception {
        if (StrUtil.isBlank(roleIdStr)){
            throw new ValidateException("角色不存在");
        }
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        String[] roleIds = roleIdStr.split(",");
        actionDao.findLink(action, ActionInfo.FIELD_ROLES);
        List<String> roleIdList = this.getRoleIdJoin(roleIds, action.getRoles());
        if (roleIdList.size() > 0){
            roleActionDao.clear(Cnd.where(RoleAction.FIELD_ACTIONID, "=", actionId)
                    .and(RoleAction.FIELD_ROLEID, "in", roleIdList.toArray(new String[roleIdList.size()])));
        }
    }

    /**
     * 已关联权限的人员数量
     * @return
     */
    public int getJoinedUsersCount(String actionId, String userName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        return userDao.getCountJoinAction(actionId, cri);
    }

    /**
     * 未关联权限的人员数量
     * @return
     */
    public int getNoJoinUsersCount(String actionId, String userName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        return userDao.getCountNotJoinAction(actionId, cri);
    }

    /**
     * 已关联人员
     * @return
     * @throws Exception
     */
    public List<UserInfo> getJoinedUsers(String actionId, String userName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        PagerUtil.getSearchOrder(cri, UserInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return userDao.getJoinAction(actionId, cri, currentPage, pageSize);
    }

    /**
     * 未已关联人员
     * @return
     * @throws Exception
     */
    public List<UserInfo> getNoJoinUsers(String actionId, String userName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        PagerUtil.getSearchOrder(cri, UserInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return userDao.getNotJoinAction(actionId, cri, currentPage, pageSize);
    }

    /**
     * 关联人员和权限
     * @param actionId 被关联的权限
     * @param userIdStr 待关联人员列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinUser(String actionId, String userIdStr) throws Exception {
        if (StrUtil.isBlank(userIdStr)){
            throw new ValidateException("用户不存在");
        }
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        List<UserInfo> joiningUsers = userDao.search(Cnd.where(UserInfo.FIELD_ID, "in", userIdStr.split(",")));
        if (joiningUsers.size() > 0){
            actionDao.findLink(action, ActionInfo.FIELD_USERS);
            List<UserInfo> users = this.getUserIdNotJoin(joiningUsers, action.getUsers());
            if (users.size() > 0){
                action.setUsers(users);
                actionDao.insertRelation(action, ActionInfo.FIELD_USERS);
            }
        }
    }

    /**
     * 取消关联人员和权限
     * @param actionId 被关联的权限
     * @param userIdStr 被关联人员列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelJoinUser(String actionId, String userIdStr) throws Exception {
        if (StrUtil.isBlank(userIdStr)){
            throw new ValidateException("用户不存在");
        }
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        List<UserInfo> joinedUsers = userDao.search(Cnd.where(UserInfo.FIELD_ID, "in", userIdStr.split(",")));
        if (joinedUsers.size() > 0){
            List<String> joinedUserIds = new ArrayList<String>();
            for (UserInfo joinedUser : joinedUsers) {
                joinedUserIds.add(joinedUser.getId());
            }
            userActionDao.clear(Cnd.where(UserAction.FIELD_USERID, "in", joinedUserIds.toArray(new String[joinedUserIds.size()]))
                    .and(UserAction.FIELD_ACTIONID, "=", actionId));
        }
    }

    /**
     * 已关联权限的资源数量
     * @return
     */
    public int getJoinedResCount(String actionId, String resName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(resName)) {
            cri.where().and(
                    Cnd.exps(ResourceInfo.FIELD_DISPLAYNAME, "like", "%" + resName + "%")
                            .or(ResourceInfo.FIELD_CODE, "like", "%" + resName + "%"));
        }
        return resDao.getCountJoinAction(actionId, cri);
    }

    /**
     * 未关联权限的资源数量
     * @return
     */
    public int getNoJoinResCount(String actionId, String resName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(resName)) {
            cri.where().and(
                    Cnd.exps(ResourceInfo.FIELD_DISPLAYNAME, "like", "%" + resName + "%")
                            .or(ResourceInfo.FIELD_CODE, "like", "%" + resName + "%"));
        }
        return resDao.getCountNotJoinAction(actionId, cri);
    }

    /**
     * 已关联资源
     * @return
     * @throws Exception
     */
    public List<ResourceInfo> getJoinedRes(String actionId, String resName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(resName)) {
            cri.where().and(
                    Cnd.exps(ResourceInfo.FIELD_DISPLAYNAME, "like", "%" + resName + "%")
                            .or(ResourceInfo.FIELD_CODE, "like", "%" + resName + "%"));
        }
        PagerUtil.getSearchOrder(cri, ResourceInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return resDao.getJoinAction(actionId, cri, currentPage, pageSize);
    }

    /**
     * 未已关联资源
     * @return
     * @throws Exception
     */
    public List<ResourceInfo> getNoJoinRes(String actionId, String resName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(resName)) {
            cri.where().and(
                    Cnd.exps(ResourceInfo.FIELD_DISPLAYNAME, "like", "%" + resName + "%")
                            .or(ResourceInfo.FIELD_CODE, "like", "%" + resName + "%"));
        }
        PagerUtil.getSearchOrder(cri, ResourceInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return resDao.getNotJoinAction(actionId, cri, currentPage, pageSize);
    }

    /**
     * 关联权限和资源
     * @param actionId 被关联的权限
     * @param resIdStr 待关联资源列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinRes(String actionId, String resIdStr) throws Exception {
        if (StrUtil.isBlank(resIdStr)){
            throw new ValidateException("资源不存在");
        }
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        List<ResourceInfo> joiningReses = resDao.search(Cnd.where(ResourceInfo.FIELD_ID, "in", resIdStr.split(",")));
        if (joiningReses.size() > 0){
            actionDao.findLink(action, ActionInfo.FIELD_RESES);
            List<ResourceInfo> reses = this.getResIdNotJoin(joiningReses, action.getReses());
            if (reses.size() > 0){
                action.setReses(reses);
                actionDao.insertRelation(action, ActionInfo.FIELD_RESES);
            }
        }
    }

    /**
     * 取消关联权限和资源
     * @param actionId 被关联的权限
     * @param resIdStr 被关联资源列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelJoinRes(String actionId, String resIdStr) throws Exception {
        if (StrUtil.isBlank(resIdStr)){
            throw new ValidateException("资源不存在");
        }
        ActionInfo action = actionDao.get(actionId);
        if (null == action){
            throw new ValidateException("权限不存在");
        }
        List<ResourceInfo> joinedReses = resDao.search(Cnd.where(ResourceInfo.FIELD_ID, "in", resIdStr.split(",")));
        if (joinedReses.size() > 0){
            List<String> joinedResIds = new ArrayList<String>();
            for (ResourceInfo joinedRes : joinedReses) {
                joinedResIds.add(joinedRes.getId());
            }
            actionResDao.clear(Cnd.where(ActionResource.FIELD_RESOURCEID, "in", joinedResIds.toArray(new String[joinedResIds.size()]))
                    .and(ActionResource.FIELD_ACTIONID, "=", actionId));
        }
    }

    private void getSearchCnd(Criteria cri, String name, String parentId){
        if (StrUtil.isNotBlank(name)){
            cri.where().and(
                    Cnd.exps(ActionInfo.FIELD_DISPLAYNAME, "like", "%" + name + "%"));
        }
        if (StrUtil.isNotBlank(parentId) && !"-1".equals(parentId)){
            cri.where().and(ActionInfo.FIELD_PARENTID, "=", parentId);
        }
    }

    /**
     * 获得传入的ID中未关联的角色
     * @param roleIds
     * @param joinRoles
     * @return
     */
    private List<String> getRoleIdNotJoin(String[] roleIds, List<RoleInfo> joinRoles){
        List<String> roleIdList = new ArrayList<String>();
        for (String roleId : roleIds) {
            boolean isIn = false;
            for (RoleInfo role : joinRoles) {
                if (role.getId().equals(roleId)){
                    isIn = true;
                    break;
                }
            }
            if (!isIn){
                roleIdList.add(roleId);
            }
        }
        return roleIdList;
    }

    /**
     * 获得传入的ID中已关联的角色
     * @param roleIds
     * @param joinRoles
     * @return
     */
    private List<String> getRoleIdJoin(String[] roleIds, List<RoleInfo> joinRoles){
        List<String> roleIdList = new ArrayList<String>();
        for (String roleId : roleIds) {
            boolean isIn = false;
            for (RoleInfo role : joinRoles) {
                if (role.getId().equals(roleId)){
                    isIn = true;
                    break;
                }
            }
            if (isIn){
                roleIdList.add(roleId);
            }
        }
        return roleIdList;
    }

    /**
     * 获得传入的ID中未关联的人员
     */
    private List<UserInfo> getUserIdNotJoin(List<UserInfo> joiningUsers, List<UserInfo> joinedUsers){
        List<UserInfo> users = new ArrayList<UserInfo>();
        for (UserInfo joiningUser : joiningUsers) {
            boolean isIn = false;
            for (UserInfo joinedUser : joinedUsers) {
                if (joinedUser.getId().equals(joiningUser.getId())){
                    isIn = true;
                    break;
                }
            }
            if (!isIn){
                users.add(joiningUser);
            }
        }
        return users;
    }

    /**
     * 获得传入的ID中未关联的资源
     */
    private List<ResourceInfo> getResIdNotJoin(List<ResourceInfo> joiningReses, List<ResourceInfo> joinedReses){
        List<ResourceInfo> reses = new ArrayList<ResourceInfo>();
        for (ResourceInfo joiningRes : joiningReses) {
            boolean isIn = false;
            for (ResourceInfo joinedRes : joinedReses) {
                if (joinedRes.getId().equals(joiningRes.getId())){
                    isIn = true;
                    break;
                }
            }
            if (!isIn){
                reses.add(joiningRes);
            }
        }
        return reses;
    }

    private void checkActionInsertInput(String creatorId, String displayName, String parentId, String remark) throws Exception {
        if (ValidatorUtils.isEmpty(displayName)){
            throw new ValidateException("名称不能为空");
        }
        if(displayName.getBytes().length < 4 || displayName.getBytes().length > 50){
            throw new ValidateException("名称应由4-50个字符组成");
        }
        if(StrUtil.isNotBlank(remark) && (remark.getBytes().length < 4 || remark.getBytes().length > 100)){
            throw new ValidateException("备注应由4-100个字符组成");
        }
        if (ValidatorUtils.isEmpty(parentId)){
            throw new ValidateException("所属父级不能为空");
        }
        if (ValidatorUtils.isEmpty(creatorId)){
            throw new ValidateException("创建者不能为空");
        }
    }

    private void checkActionUpdateInput(String displayName, String parentId, String remark) throws Exception {
        if (ValidatorUtils.isEmpty(displayName)){
            throw new ValidateException("名称不能为空");
        }
        if(displayName.getBytes().length < 4 || displayName.getBytes().length > 50){
            throw new ValidateException("名称应由4-50个字符组成");
        }
        if(StrUtil.isNotBlank(remark) && (remark.getBytes().length < 4 || remark.getBytes().length > 100)){
            throw new ValidateException("备注应由4-100个字符组成");
        }
        if (ValidatorUtils.isEmpty(parentId)){
            throw new ValidateException("所属父级不能为空");
        }
    }
}
